package com.pi.shisan.service.audio;

import com.pi.shisan.service.util.ComponentManager;
import com.pi.shisan.service.util.DspProcessorCallBackFactory;
import com.pi.shisan.service.util.SecondRefreshThreadUtil;
import de.quippy.javamod.mixer.dsp.AudioProcessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.sound.sampled.AudioFormat;
import java.io.File;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;

@Slf4j
public abstract class AudioPlayer implements AudioLevelManager.AudioMixer{

    protected AudioEffectComponentsSupplier<ComponentManager.MusicEffectBaseComponent> musicEffectComponentsSupplier;
    protected ComponentManager.MusicEffectBaseComponent musicEffectComponents;
    protected File audioFile;
    private int repeatTimes;
    private int repeatCount;
    private String title;

    /**
     * 同步的
     * 如果播放器正常播放完毕音乐会调用此对象
     */
    protected Runnable onNormalFinishedListener;

    protected Consumer<Integer> playBeforeVolume;

    protected final static int INIT = 0;
    protected final static int PLAYING = 1;
    protected final static int INTERRUPTED = 2;
    protected final static int DESTROYED = 3;


    /**
     * 0 - 初始状态
     * 1 - 正常播放
     * 2 - 中断（暂停）
     * 3 - 结束（播放完毕或停止）
     * 快进功能暂不支持
     */
    protected final AtomicInteger playerStatus = new AtomicInteger(0);

    /**
     * 0 - 正常
     * 1 - 正在reset
     * 2 - 正在destroy, 无法再reset
     * 目的：阻止reset和destroy同时进行，因为可能会导致资源不能正确关闭
     */
    protected final AtomicInteger resetStatus = new AtomicInteger(0);
    protected static final byte[] EMPTY_DATA = new byte[0];

    /**
     * 正常是0，
     * 调用destroy时改为1
     * destroy调用完毕改为2
     * playOrResume 等待将2改为3，改成功后退出
     */
    protected final AtomicInteger releaseStatus = new AtomicInteger(0);

    /**
     * 音频总时长
     */
    protected long totalMilliseconds;
    protected long currentMilliseconds;

    private Integer volume;

    protected Supplier<Boolean> timeUpdater;

    protected AudioOutputStream audioOutputStream;

    private byte[] buffOutput;

    public AudioPlayer(File audioFile, AudioEffectComponentsSupplier<ComponentManager.MusicEffectBaseComponent> musicEffectComponentsSupplier) throws Exception {
        this.audioFile = audioFile;
        this.musicEffectComponentsSupplier = musicEffectComponentsSupplier;
    }

    public void setOnNormalFinisedListener(Runnable onNormalFinishedListener) {
        this.onNormalFinishedListener = onNormalFinishedListener;
    }

    public void setPlayBeforeVolume(Consumer<Integer> playBeforeVolume) {
        this.playBeforeVolume = playBeforeVolume;
    }

    public Integer getVolume() {
        return volume;
    }

    public void setVolume(Integer volume) {
        this.volume = volume;
//        if (this.playBeforeVolume != null) {
//            this.playBeforeVolume.accept(volume);
//        }
    }

    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * 设置重复播放次数，-1表示单曲循环
     * @param repeatTimes
     */
    public void setRepeatTimes(int repeatTimes) {
        this.repeatTimes = repeatTimes;
    }

    public File getAudioFile() {
        return this.audioFile;
    }

    /**
     * 发生在： return 结果之后，判断结果之前。如果return结果不为true，但是判断结果前被改为true
     * @return
     */
    @Override
    public boolean playOrResume() {
        try {
            if (playerStatus.compareAndSet(INIT, PLAYING)
                    || playerStatus.compareAndSet(INTERRUPTED, PLAYING)
            ) {
                log.debug("开始播放：{}", audioFile);
                if (audioOutputStream != null) {
                    audioOutputStream.resume();
                }
                if (musicEffectComponentsSupplier != null) {
                    ComponentManager.MusicEffectBaseComponent musicEffectComponents = musicEffectComponentsSupplier.get();
                    this.musicEffectComponents = musicEffectComponents;
                    if (musicEffectComponentsSupplier.open()) {
                        if (musicEffectComponents.getProcessStateComponent() != null) {
                            musicEffectComponents.getProcessStateComponent().setMax(totalMilliseconds);
                        }
                    }
                }

                if (musicEffectComponents != null && musicEffectComponents.getScrollTextComponent() != null && StringUtils.isNotBlank(title)) {
                    musicEffectComponents.getScrollTextComponent().setText(title);
                }
                if (timeUpdater == null && musicEffectComponentsSupplier != null && musicEffectComponentsSupplier.open() && musicEffectComponents != null) {
                    timeUpdater = ()->{
                        if (totalMilliseconds > 0) {
                            if (musicEffectComponents.getTimeFormatComponent() != null) {
                                musicEffectComponents.getTimeFormatComponent().setVal((int) (currentMilliseconds / 1000));
                            }
                            if (musicEffectComponents.getProcessStateComponent() != null) {
                                musicEffectComponents.getProcessStateComponent().setVal(currentMilliseconds);
                            }
                        }
                        return playerStatus.get() != DESTROYED;
                    };
                }
                if (timeUpdater != null) {
                    SecondRefreshThreadUtil.register(timeUpdater);
                }
                if (playBeforeVolume != null) {
                    playBeforeVolume.accept(this.volume);
                }
                while (doingPlay()) {
                    if (repeatTimes < 0 || repeatCount < repeatTimes) {
                        if (resetStatus.compareAndSet(0,1)) {
                            repeatCount++;
                            try {
                                reset();
                            } finally {
                                resetStatus.set(0);
                            }
                        }
                        continue;
                    }
                    destroy();
                    if (onNormalFinishedListener != null) {
                        try {
                            onNormalFinishedListener.run();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            destroy();
        } finally {
            if (timeUpdater != null) {
                SecondRefreshThreadUtil.unregister(timeUpdater);
            }
        }
        while (!releaseStatus.compareAndSet(2,3)) {
            if (releaseStatus.get() == 0 || releaseStatus.get() == 3) {
                break;
            }
            Thread.yield();
        }
        //表示已经播放完成
        return playerStatus.get() == DESTROYED;
    }

    @Override
    public void interrupt() {
        System.out.println("start interrupt  " + audioFile);
        while (!playerStatus.compareAndSet(INIT,INTERRUPTED)
            && !playerStatus.compareAndSet(PLAYING, INTERRUPTED)) {
            if (playerStatus.get() == DESTROYED) {
                return;
            }
            if (playerStatus.get() == INTERRUPTED) {
                break;
            }
            Thread.yield();
        }
        if (audioOutputStream != null) {
            audioOutputStream.interrupt();
        }
        System.out.println("interrupted...  " + audioFile);
    }

    @Override
    public void destroy() {
        releaseStatus.set(1);
        System.out.println("start destroy  " + audioFile);
        while (!resetStatus.compareAndSet(0,2)) {
            if (resetStatus.get() == 2) {
                break;
            }
            Thread.yield();
        }
        playerStatus.set(DESTROYED);
        System.out.println("destroyed  " + audioFile);
        doDestroy();
        closeSound();
        releaseStatus.set(2);
    }

    public boolean isDestroy() {
        return playerStatus.get() == DESTROYED;
    }

    protected abstract void doDestroy();


    /**
     * 如果正常的播放完毕，则返回true，否则返回false
     * @return
     * @throws Exception
     */
    protected abstract boolean doingPlay() throws Exception;

    /**
     * 如果需要重复播放，则会调用reset，而且任务不会因此结束
     * @return
     * @throws Exception
     */
    protected abstract void reset() throws Exception;

    protected synchronized void openSound(AudioFormat audioFormat) {
        if (playerStatus.get() == DESTROYED) {
            return;
        }
        audioOutputStream = new AudioOutputStream(audioFormat, initAudioProcessor());
    }

    protected void writeSampleData(byte[] data) {
        audioOutputStream.writeSampleData(data,0,data.length);
    }

    protected void writeSampleData(byte[] data, int start, int len) {
        audioOutputStream.writeSampleData(data,start,len);
    }

    private synchronized void closeSound() {
        if (audioOutputStream != null) {
            try {
                audioOutputStream.close();
            } catch (Exception e) {
            }
            audioOutputStream = null;
        }
    }

    protected byte[] getOutputBuffer(int length) {
        if (buffOutput == null || buffOutput.length < length) {
            buffOutput = new byte[length];
        }
        return buffOutput;
    }

    private AudioProcessor initAudioProcessor() {
        AudioProcessor audioProcessor = null;
        if (musicEffectComponentsSupplier != null && musicEffectComponentsSupplier.open()) {
            audioProcessor = new AudioProcessor(2048, 70);
            audioProcessor.addListener(DspProcessorCallBackFactory.getDspProcessorCallback(musicEffectComponents));
        }
        return audioProcessor;
    }

    public long getTotalMilliseconds() {
        return totalMilliseconds;
    }
}
